package com.pachong.android.baseuicomponent.refreshable.pullrefresh;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

import com.pachong.android.baseuicomponent.R;

import java.util.Timer;
import java.util.TimerTask;


/**
 * 下拉刷新Layout, 把View实现IPullable接口,然后放入其中即可
 * <p>
 * 来源: http://blog.csdn.net/zhongkejingwang/article/details/38868463
 *
 * @author Vivi Peng
 * @version 1.0.1
 * @since 2016-01-29
 */
public class PullToRefreshLayout extends RelativeLayout {
    public static final String TAG = PullToRefreshLayout.class.getSimpleName();
    // 初始状态
    public static final int STATE_IDLE = 0;
    // 释放刷新
    public static final int STATE_RELEASE_TO_REFRESH = 1;
    // 正在刷新
    public static final int STATE_REFRESHING = 2;
    // 操作完毕
    public static final int STATE_DONE = 5;
    /**
     * 当前状态
     * 注意:这个只能代表刷新UI的状态, 不能作为数据加载的状态. 因为有这么一个case:需求要求刷新的UI至少展示一秒,所以就出现了
     * 在0.5秒的时候数据已经获取到,是finish的状态,但是UI必须展示刷新的状态.
     */
    private int mPullState = STATE_IDLE;

    // 刷新回调接口
    private OnRefreshListener mListener;

    // 按下Y坐标，上一个事件点Y坐标
    private float mCurrY, mLastY;

    // 下拉的距离。注意：pullDownY和pullUpY不可能同时不为0
    public float mPullDistance = 0;

    // 释放刷新的距离
    private float refreshDist = 200;

    private MyTimer timer;
    // 回滚速度
    public float MOVE_SPEED = 8;

    // 在刷新过程中滑动操作
    private boolean isTouch = false;
    // 手指滑动距离与下拉头的滑动距离比，中间会随正切函数变化
    private float radio = 2;


    // 下拉头
    private View mPullContainer;
    // 下拉状态展示的View
    private View mPullStateView;
    // 刷新状态展示的View
    private View mRefreshingStateView;

    // 实现了Pullable接口的View
    private View mPullableView;

    // 过滤多点触碰
    private int mEvents;


    /**
     * 下来刷新的开关是否打开
     */
    private boolean mIsPullEnable = false;


    /**
     * 执行自动回滚的handler
     */
    private Handler updateHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            // 回弹速度随下拉距离moveDeltaY增大而增大
            MOVE_SPEED = (float) (8 + 5 * Math.tan(Math.PI / 2
                    / getMeasuredHeight() * (mPullDistance + Math.abs(0))));
            if (!isTouch) {
                // 正在刷新，且没有往上推的话则悬停，显示"正在刷新..."
                if (mPullState == STATE_REFRESHING && mPullDistance <= refreshDist) {
                    mPullDistance = refreshDist;
                    timer.cancel();
                }
            }
            if (mPullDistance > 0)
                mPullDistance -= MOVE_SPEED;
            if (mPullDistance < 0) {
                // 已完成回弹
                mPullDistance = 0;
                // 隐藏下拉头时有可能还在刷新，只有当前状态不是正在刷新时才改变状态
                if (mPullState != STATE_REFRESHING)
                    changeState(STATE_IDLE);
                timer.cancel();
                requestLayout();
            }

            // 刷新布局,会自动调用onLayout
            requestLayout();
            // 没有拖拉或者回弹完成
            if (mPullDistance + Math.abs(0) == 0)
                timer.cancel();
        }

    };

    public void setOnRefreshListener(OnRefreshListener listener) {
        mListener = listener;
    }

    public PullToRefreshLayout(Context context) {
        super(context);
        initView();
    }

    public PullToRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView();
    }

    private void initView() {
        timer = new MyTimer(updateHandler);
    }

    private void hide() {
        timer.schedule(5);
    }

    /**
     * 完成刷新操作，显示刷新结果。注意：刷新完成后一定要调用这个方法
     */
    public void refreshFinish() {
        mRefreshingStateView.clearAnimation();
        mRefreshingStateView.setVisibility(View.VISIBLE);
        if (mPullDistance > 0) {
            changeState(STATE_DONE);
            hide();
        } else {
            changeState(STATE_DONE);
            hide();
        }
    }


    /**
     * 改变下拉UI的状态
     * <p>
     * 主要就是几个UI的显示与隐藏
     *
     * @param toState
     */
    private void changeState(int toState) {
        if (mPullState != toState) {
            mPullState = toState;
            switch (mPullState) {
                case STATE_IDLE:
                    // 下拉布局初始状态
                    if (mPullStateView != null) {
                        mPullStateView.setVisibility(View.VISIBLE);
                    }
                    if (mRefreshingStateView != null) {
                        mRefreshingStateView.setVisibility(GONE);
                    }

                    // 上拉布局初始状态

                    break;
                case STATE_RELEASE_TO_REFRESH:
                    // 释放刷新状态
                    if (mRefreshingStateView != null) {
                        mRefreshingStateView.setVisibility(GONE);
                    }
                    break;
                case STATE_REFRESHING:
                    // 正在刷新状态
                    if (mPullStateView != null) {
                        mPullStateView.setVisibility(View.GONE);
                    }

                    if (mRefreshingStateView != null) {
                        mRefreshingStateView.setVisibility(VISIBLE);
                    }
                    break;
                case STATE_DONE:
                    // 刷新或加载完毕，啥都不做
                    break;
            }
        }
    }


    /**
     * 下来刷新开关, 默认关闭
     * @param enable
     */
    public void setRefreshEnabled (boolean enable) {
        if(mIsPullEnable != enable) {
            mIsPullEnable = enable;
        }
    }


    /**
     * 由父控件决定是否分发事件，防止事件冲突
     *
     * @see ViewGroup#dispatchTouchEvent(MotionEvent)
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if(mIsPullEnable == false) {
            super.dispatchTouchEvent(ev);
            return true;
        }
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mCurrY = ev.getY();
                mLastY = mCurrY;
                timer.cancel();
                mEvents = 0;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
            case MotionEvent.ACTION_POINTER_UP:
                // 过滤多点触碰
                mEvents = -1;
                break;
            case MotionEvent.ACTION_MOVE:
                if (mEvents == 0) {
                    if (mPullDistance > 0 || (((Pullable) mPullableView).canPullDown())) {
                        // 可以下拉，正在加载时不能下拉
                        // 对实际滑动距离做缩小，造成用力拉的感觉
                        mPullDistance = mPullDistance + (ev.getY() - mLastY) / radio;
                        if (mPullDistance < 0) {
                            mPullDistance = 0;
                        }
                        if (mPullDistance > getMeasuredHeight()) {
                            mPullDistance = getMeasuredHeight();
                        }
                        if (mPullState == STATE_REFRESHING) {
                            // 正在刷新的时候触摸移动
                            isTouch = true;
                        }
                    }
                } else {
                    mEvents = 0;
                }
                mLastY = ev.getY();
                // 根据下拉距离改变比例
                radio = (float) (2 + 2 * Math.tan(Math.PI / 2 / getMeasuredHeight()
                        * (mPullDistance + Math.abs(0))));

                if (mPullDistance > 0) {
                    requestLayout();
                    if (mPullDistance <= refreshDist
                            && (mPullState == STATE_RELEASE_TO_REFRESH || mPullState == STATE_DONE)) {
                        // 如果下拉距离没达到刷新的距离且当前状态是释放刷新，改变状态为下拉刷新
                        changeState(STATE_IDLE);
                    }
                    if (mPullDistance >= refreshDist && mPullState == STATE_IDLE) {
                        // 如果下拉距离达到刷新的距离且当前状态是初始状态刷新，改变状态为释放刷新
                        changeState(STATE_RELEASE_TO_REFRESH);
                    }
                }

                // 因为刷新和加载操作不能同时进行，所以pullDownY和pullUpY不会同时不为0，因此这里用(mPullDistance +
                // Math.abs(pullUpY))就可以不对当前状态作区分了
                if ((mPullDistance + Math.abs(0)) > 8) {
                    // 防止下拉过程中误触发长按事件和点击事件
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                }
                break;

            case MotionEvent.ACTION_UP:
                Log.e("pww", "state == " + mPullState);
                if (mPullDistance > refreshDist) {
                    // 正在刷新时往下拉（正在加载时往上拉），释放后下拉头（上拉头）不隐藏
                    isTouch = false;
                }
                if (mPullState == STATE_RELEASE_TO_REFRESH) {
                    changeState(STATE_REFRESHING);
                    // 刷新操作
                    if (mListener != null) {
                        mListener.onRefresh(this);
                    }
                }
                hide();
            default:
                break;
        }
        // 事件分发交给父类
        super.dispatchTouchEvent(ev);
        return true;
    }

    // 第一次执行布局
    private boolean mIsLayouted = false;

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (!mIsLayouted) {
            // 这里是第一次进来的时候做一些初始化
            mIsLayouted = true;
            refreshDist = ((ViewGroup) mPullContainer).getChildAt(0)
                    .getMeasuredHeight();
        }
        // 改变子控件的布局，这里直接用(mPullDistance + pullUpY)作为偏移量，这样就可以不对当前状态作区分
        float pullUpY = 0;
        mPullContainer.layout(0,
                (int) (mPullDistance + pullUpY) - mPullContainer.getMeasuredHeight(),
                mPullContainer.getMeasuredWidth(), (int) (mPullDistance + pullUpY));
        mPullableView.layout(0, (int) (mPullDistance + pullUpY),
                mPullableView.getMeasuredWidth(), (int) (mPullDistance + pullUpY)
                        + mPullableView.getMeasuredHeight());
    }


    /**
     * 设置Views, 默认构造函数里面无法获取,会为null
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mPullContainer = getChildAt(0);
        mPullableView = getChildAt(1);
        mPullStateView = mPullContainer.findViewById(R.id.pullIcon);
        mRefreshingStateView = mPullContainer.findViewById(R.id.refreshingIcon);
    }

    class MyTimer {
        private Handler handler;
        private Timer timer;
        private MyTask mTask;

        public MyTimer(Handler handler) {
            this.handler = handler;
            timer = new Timer();
        }

        public void schedule(long period) {
            if (mTask != null) {
                mTask.cancel();
                mTask = null;
            }
            mTask = new MyTask(handler);
            timer.schedule(mTask, 0, period);
        }

        public void cancel() {
            if (mTask != null) {
                mTask.cancel();
                mTask = null;
            }
        }

        class MyTask extends TimerTask {
            private Handler handler;

            public MyTask(Handler handler) {
                this.handler = handler;
            }

            @Override
            public void run() {
                handler.obtainMessage().sendToTarget();
            }

        }
    }

    /**
     * 刷新加载回调接口
     *
     * @author chenjing
     */
    public interface OnRefreshListener {
        /**
         * 刷新操作
         */
        void onRefresh(PullToRefreshLayout pullToRefreshLayout);
    }

}
